﻿using System;
using System.Collections.Generic;
using System.IO;
using CSJ2K.Util.IO;
using CSJ2K.Jpc.Segments;

namespace CSJ2K.Jpc
{
    public abstract class Segment
    {
        /// <summary>Flags indicating the presence of defined precincts and SOP/EPH markers.</summary>
        public enum CodingStyles : byte
        {
            /// <summary>Entropy coder precincts defined below. Otherwise maximal with PPx=15 and PPy=15.</summary>
            Defined = 0x01,	    
            
            /// <summary>SOP marker segments may be used.</summary>
            SOP = 0x02,	     

            /// <summary>EPH marker shall be used.</summary>
            EPH = 0x04	        
        }

        /// <summary>Progression order for the SGcod, SPcoc and Ppoc parameters</summary>
        public enum ProgressionOrders : byte
        {
            /// <summary>Layer-Resolution level-Component-Position</summary>
            LRCP=0x00,

            /// <summary>Resolution level-Layer-Component-Position</summary>
            RLCP=0x01,

            /// <summary>Resolution level-Position-Component-Layer</summary>
            RPCL=0x02,

            /// <summary>Position-Component-Resolution level-Layer</summary>
            PCRL=0x03,

            /// <summary>Component-Position-Resolution level-Layer</summary>
            CPRL=0x04,
        }

        /// <summary>Code-block style for the SPcod and SPcoc parameters</summary>
        public enum CodeBlockStyles : byte
        {
            /// <summary>Selective arithmetic coding bypass</summary>
            SelectiveArithmetic=0x01,

            /// <summary>Reset context probabilities on coding pass boundaries</summary>
            ResetContext=0x02,          

            /// <summary>Termination on each coding pass</summary>
            Termination=0x04,           

            /// <summary>Vertically causal context</summary>
            VerticallyCausal=0x08,      

            /// <summary>Predictable termination</summary>
            Predictable=0x10,           

            /// <summary>Segmentation symbols are used</summary>
            Symbols=0x20                
        }

        /// <summary>Transformation for the SPcod and SPcoc parameters</summary>
        public enum Transformations : byte
        {
            Irreversible=0x00,  // 9-7 irreversible filter
            Reversible=0x01     // 5-3 reversible filter
        }

        /// <summary>Marker Codes for all Marker Segments</summary>
        public enum Markers : ushort
        {
            // Delimiting Marker Segments
            SOC = 0xFF4F,	// Start of codestream
            SOT = 0xFF90,	// Start of tile-part
            SOD = 0xFF93,	// Start of data
            EOC = 0xFFD9,	// End of Codestream

            // Fixed Information Marker Segments
            SIZ = 0xFF51,	// Image and tile size

            // Functional Marker Segments
            COD = 0xFF52,	// Coding style default
            COC = 0xFF53,	// Coding style component
            RGN = 0xFF5E,	// Region-of-interest
            QCD = 0xFF5C,	// Quantization default
            QCC = 0xFF5D,	// Quantization component
            POC = 0xFF5F,	// Progression order change

            // Pointer marker segments
            PLM = 0xFF57,	// Tile-part lengths
            PLT = 0xFF58,	// Packet length, main header
            PPM = 0xFF60,	// Packet length, tile-part header
            PPT = 0xFF61,	// Packed packet headers, main header
            TLM = 0xFF55,	// Packed packet headers, tile-part header

            // In-bit-stream marker segments
            SOP = 0xFF91,	// Start of packet
            EPH = 0xFF92,	// End of packet header

            // Informational marker segments
            CRG = 0xFF63,	// Component registration
            COM = 0xFF64	// Comment
        }

        /// <summary>Marker Segment Code</summary>
        public Markers Marker;

        public long Start;

        /// <summary>Length of marker segment in bytes. (not including the marker)</summary>
        public ushort Length;      

        protected Segment(Stream source, Markers marker, bool hasLength)
        {
            Start = source.Position - 2;
            Marker = marker;

            if (hasLength) Length = StreamUtil.ReadBUInt16(source);
            else Length = 0;
        }
        // If we haven't read the Marker yet, do that first.
        public static Segment FromStream(Stream source, int components)
        {
            Markers Marker = (Markers)StreamUtil.ReadBUInt16(source);
            return FromStream(source, Marker, components);
        }
        // If we already have the marker, create the appropriate type to read in the Segment.
        public static Segment FromStream(Stream source, Markers marker, int components)
        {
            switch (marker)
            {
                // Delimiting Marker Segments
                case Markers.SOC:
                    return ((Segment)new SOC(source, marker));
                case Markers.SOT:
                    return ((Segment)new SOT(source, marker));
                case Markers.SOD:
                    return ((Segment)new SOD(source, marker));
                case Markers.EOC:
                    return ((Segment)new EOC(source, marker));

                // Fixed Information Marker Segments
                case Markers.SIZ:
                    return ((Segment)new SIZ(source, marker));

                // Functional Marker Segments
                case Markers.COD:
                    return ((Segment)new COD(source, marker));
                case Markers.COC:
                    return ((Segment)new COC(source, marker, components));
                case Markers.RGN:
                    return ((Segment)new RGN(source, marker));
                case Markers.QCD:
                    return ((Segment)new QCD(source, marker));
                case Markers.QCC:
                    return ((Segment)new QCC(source, marker, components));
                case Markers.POC:
                    return ((Segment)new POC(source, marker));

                // Pointer marker segments
                case Markers.TLM:
                    return ((Segment)new TLM(source, marker));
                case Markers.PLM:
                    return ((Segment)new PLM(source, marker));
                case Markers.PLT:
                    return ((Segment)new PLT(source, marker));
                case Markers.PPM:
                    return ((Segment)new PPM(source, marker));
                case Markers.PPT:
                    return ((Segment)new PPT(source, marker));

                // In-bit-stream marker segments
                case Markers.SOP:
                    return ((Segment)new SOP(source, marker));
                case Markers.EPH:
                    return ((Segment)new EPH(source, marker));

                // Informational marker segments
                case Markers.CRG:
                    return ((Segment)new CRG(source, marker));
                case Markers.COM:
                    return ((Segment)new COM(source, marker));

                default: // Invalid or unknown Marker
                    throw(new ApplicationException("Unknown Marker Type: " + marker.ToString("X")));
            }
        }
    }
}
